package com.mihuspring.www.context;

import com.mihuspring.www.context.annotation.*;
import com.mihuspring.www.core.BeanDefinition;
import org.apache.commons.lang.StringUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class AnnotationConfigApplicationContext implements ApplicationContext {
  Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();

  Object config;
  public AnnotationConfigApplicationContext(Class t){
    if(!t.isAnnotationPresent(Configuration.class)){
      throw new RuntimeException("config class is invalid");
    }
    if(t.isAnnotationPresent(ComponentScan.class)){
      ComponentScan componentScanAnnotation = (ComponentScan) t.getAnnotation(ComponentScan.class);
      String basePackage = componentScanAnnotation.value();
      ClassContext classContext = new ClassContext(basePackage);
      Set<Class<?>> componentClassSet = classContext.listClass(new AnnotationClassFilter(Component.class));
      for(Class<?> cla : componentClassSet){
        Constructor c;
        try{
          c = cla.getDeclaredConstructor();
        }catch (Exception err){
          throw new RuntimeException(err);
        }
        Component component = cla.getAnnotation(Component.class);
        String packageName = component.value();
        if(StringUtils.isEmpty(packageName)){
          String fullName = cla.getName();

          char[] chars = fullName.substring(fullName.lastIndexOf('.')+1).toCharArray();
          chars[0] += 32;
          packageName = String.valueOf(chars);
        }
        BeanDefinition beanDefinition = new BeanDefinition(c);
        beanDefinitionMap.put(packageName, beanDefinition);
      }
    }
    try{
      config = t.newInstance();
    }catch (Exception err){
      throw new RuntimeException(err);
    }
    for(Method method : t.getMethods()){
      if(method.isAnnotationPresent(Bean.class)){
        String name;
        Bean beanAnnotation = method.getAnnotation(Bean.class);
        if(StringUtils.isNotEmpty(beanAnnotation.name())){
          name = beanAnnotation.name();
        }else {
          name = method.getName();
        }
        BeanDefinition beanDefinition = new BeanDefinition(config, method);
        if(method.isAnnotationPresent(Scope.class)){
          beanDefinition.setScope(method.getAnnotation(Scope.class).value());
        }
        beanDefinitionMap.put(name, beanDefinition);
      }
    }
  }
  @Override
  public Object getBean(String name){
    BeanDefinition beanDefinition = beanDefinitionMap.get(name);
    try {
      return beanDefinition.getBean();
    }catch (Exception err){
      throw new RuntimeException(err);
    }
  }

}
